﻿/*
 *  Autor: Michał Schweichler
 * e-mail: michal.schweichler@gmail.com , michal@schweichler.pl
 *    www: schweichler.pl
 * 
 *  Klasa C# wspomagająca obsługę portalu nk.pl
 */
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
//using System.Collections;

namespace NKlib
{
    /// <summary>
    /// Klasa wspomagająca obsługę portalu nk.pl bez użycia API
    /// </summary>
    public class NK
    {
        /// <summary>
        /// 
        /// </summary>
        public string[] error = new string[2];
        private string _login;
        private string _password;
        private string _userAgent;
        private CookieContainer _cookies = new CookieContainer();

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="login">Login do nk.pl</param>
        /// <param name="password">Hasło do nk.pl</param>
        /// <param name="userAgent">Opcjonalnie User-Agent naszej przeglądarki. Domyślnie ustawione jest na Mozilla Firefox 3.6.13</param>
        public NK(string login, string password, string userAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13")
        {
            this._login = login;
            this._password = password;
            this._userAgent = userAgent;
            this.error[0] = null;
            this.error[1] = "0";
        }

        /// <summary>
        /// Metoda ta pobiera nam daną stronę (wartość url) i zwraca jako string
        /// </summary>
        /// <param name="url">Adres url strony</param>
        /// <param name="referer">Referer w nagłówku HEAD- domyślnie strona główna nk.pl</param>
        /// <returns>Zwraca źródło strony (string)</returns>
        private string _getPage(string url, string referer = "http://nk.pl/main")
        {
            try
            {
                HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(url);
                webReq.ContentType = "application/x-www-form-urlencode";
                webReq.Method = "GET";
                webReq.Timeout = 4000;
                webReq.ReadWriteTimeout = 4000;
                webReq.UserAgent = this._userAgent;
                webReq.Referer = referer;
                webReq.CookieContainer = this._cookies;
                webReq.KeepAlive = true;
                webReq.Accept = "*/*";

                WebResponse webRes = webReq.GetResponse();
                Stream stream = webRes.GetResponseStream();
                string page = new StreamReader(stream).ReadToEnd();

                //Strona o podanym adresie nie istnieje
                //Profil tego użytkownika został usunięty
                //Przepraszamy, nie możesz obecnie gościć na tym profilu, ponieważ został on zablokowany
                //Nie możesz wykonać tej akcji dla tego użytkownika, ponieważ jesteś przez niego zablokowany
                if (page.Contains("Strona o podanym adresie nie istnieje"))
                {
                    this.error[0] = "Nie ma takiego profilu";
                    this.error[1] = "0";
                    return null;
                }
                else if (page.Contains("Profil tego użytkownika został usunięty"))
                {
                    this.error[0] = "Profil tego użytkownika został usunięty";
                    this.error[1] = "0";
                    return null;
                }
                else if (page.Contains("Przepraszamy, nie możesz obecnie gościć na tym profilu, ponieważ został on zablokowany"))
                {
                    this.error[0] = "Profil został zablokowany";
                    this.error[1] = "0";
                    return null;
                }
                else if (page.Contains("Nie możesz wykonać tej akcji dla tego użytkownika, ponieważ jesteś przez niego zablokowany"))
                {
                    this.error[0] = "Jesteś zablokowany przez tego użytkownika";
                    this.error[1] = "0";
                    return null;
                }
                else
                    return page;
            }
            catch (WebException wE)
            {
                HttpWebResponse wR = (HttpWebResponse)wE.Response;
                if (wR != null)
                {
                    string errorContent = new StreamReader(wR.GetResponseStream()).ReadToEnd();
                    if (errorContent.Contains("Serwery są przeciążone"))
                    {
                        Match m = Regex.Match(errorContent, "za około (.*?) sek", RegexOptions.Singleline);
                        error[0] = "Nasza klasa zablokowana na " + m.Groups[1].Value + " sekund...";
                        this.error[1] = "1";
                        return null;
                    }
                    // DO POPRAWY start - nie 'wyciąga' czasu do kiedy portal jest niedostępny
                    else if (errorContent.Contains("Trwa archiwizacja danych i porządkowanie"))
                    {
                        Console.WriteLine(errorContent);
                        Match m2 = Regex.Match(errorContent, "godziny(.*?)&hellip;", RegexOptions.Multiline);
                        //error = "Trwa archiwizacja danych i porządkowanie, portal jest niedostępny do godziny " + m2.Groups[1].Value;
                        error[0] = "Trwa archiwizacja danych i porządkowanie, portal jest niedostępny";
                        this.error[1] = "1";
                        return null;
                    }
                    // DO POPRAWY stop
                    else
                    {
                        error[0] = wR.StatusDescription; // zapisuje nam błąd
                        this.error[1] = "1";
                        return null;
                    }
                }
                else
                {
                    error[0] = wE.Message; // zapisuje nam błąd
                    this.error[1] = "1";
                    return null;
                }
            }
        }


        /// <summary>
        /// Metoda (prywatna) wysyła pod dany adres url wartości POST podane w stringu. Można ustawić referer'a.
        /// </summary>
        /// <param name="url">Adres www, np: "http://nk.pl"</param>
        /// <param name="post">Wartości POST, dokładnie takie jak wysyła dany formularz na stronie np: &quot;login=twójLogin&amp;password=twojeHasło&quot;</param>
        /// <param name="referer">Adres strony z której nas przekierował formularz np: "http://nk.pl/show_invites". Domyślnie ustawione na stronę główną nk.pl</param>
        /// <returns></returns>
        private string _sendPost(string url, string post, string referer = "http://nk.pl/main")
        {
            try
            {
                HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(url);
                webReq.Host = "nk.pl";
                webReq.ContentType = "application/x-www-form-urlencoded";
                webReq.Method = "POST";
                webReq.Timeout = 4000;
                webReq.ReadWriteTimeout = 4000;
                webReq.UserAgent = this._userAgent;
                webReq.Referer = referer;
                webReq.CookieContainer = this._cookies;
                webReq.KeepAlive = true;
                webReq.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
                //webReq.ContentLength = post.Length;
                webReq.ContentLength = Encoding.UTF8.GetBytes(post).Length;

                byte[] postBytes = Encoding.UTF8.GetBytes(post);
                BinaryWriter bW = new BinaryWriter(webReq.GetRequestStream(), Encoding.UTF8);
                bW.Write(postBytes);
                bW.Flush();
                bW.Close();

                WebResponse webRes = webReq.GetResponse();
                Stream str = webRes.GetResponseStream();

                return new StreamReader(str).ReadToEnd();
            }
            catch (WebException wE)
            {
                HttpWebResponse wR = (HttpWebResponse)wE.Response;
                if (wR != null)
                {
                    string errorContent = new StreamReader(wR.GetResponseStream()).ReadToEnd();
                    if (errorContent.Contains("Serwery są przeciążone"))
                    {
                        Match m = Regex.Match(errorContent, "za około (.*?) sek", RegexOptions.Singleline);
                        error[0] = "Nasza klasa zablokowana na " + m.Groups[1].Value + " sekund...";
                        this.error[1] = "1";
                        return null;
                    }
                    // DO POPRAWY start - nie 'wyciąga' czasu do kiedy portal jest niedostępny
                    else if (errorContent.Contains("Trwa archiwizacja danych i porządkowanie"))
                    {
                        Console.WriteLine(errorContent);
                        Match m2 = Regex.Match(errorContent, "godziny(.*?)&hellip;", RegexOptions.Multiline);
                        //error = "Trwa archiwizacja danych i porządkowanie, portal jest niedostępny do godziny " + m2.Groups[1].Value;
                        error[0] = "Trwa archiwizacja danych i porządkowanie, portal jest niedostępny";
                        this.error[1] = "1";
                        return null;
                    }
                    // DO POPRAWY stop
                    else
                    {
                        error[0] = wR.StatusDescription; // zapisuje nam błąd
                        this.error[1] = "1";
                        return null;
                    }
                }
                else
                {
                    error[0] = wE.Message; // zapisuje nam błąd
                    this.error[1] = "1";
                    return null;
                }
            }
        }

        /// <summary>
        /// Metoda, która nas loguje do portalu nk.pl
        /// Nie potrzebuje żadnych wartości, bo podawaliśmy w konstruktorze.
        /// </summary>
        /// <returns>Zwraca true jak udało się zalogować, false gdy się nie udało.</returns>
        public bool login()
        {
            string pageAfterLogin = _sendPost("http://nk.pl/login", "login=" + this._login + "&password=" + this._password, "http://nk.pl");
            if (pageAfterLogin != null)
            {
                if (pageAfterLogin.Contains("Nieprawidłowa nazwa użytkownika") == true)
                {
                    this.error[0] = "Nieprawidłowa nazwa użytkownika lub hasło";
                    this.error[1] = "1";
                    return false; // błędna nazwa użytkownika lub hasło
                }
                else
                {
                    return true; // zalogowano poprawnie
                }
            }
            else
                return false; // błąd serwera / brak połączenia / blokada od nk.pl
        }

        /// <summary>
        /// Metoda ta pobiera dane osoby na podstawie podanego ID.
        /// </summary>
        /// <param name="ID">ID osoby na nk.pl</param>
        /// <returns>Zwraca nam tablice string[10] z danymi osoby.
        /// string[0] - imię
        /// string[1] - nazwisko
        /// string[2] - nazwisko rodowe
        /// string[3] - miejscowość
        /// string[4] - wiek
        /// string[5] - płeć
        /// string[6] - numer telefonu
        /// string[7] - pseudo
        /// string[8] - liczba znajomych
        /// string[9] - liczba zdjęć
        /// 
        /// JEŻELI NIE MA DANEJ STRONY Z PODANYM ID - konto usunięte / zablokowane - ZWRACA NULL</returns>
        public string[] getPerson(int ID)
        {
            string page = _getPage("http://nk.pl/profile/" + ID);
            string[] person = new string[10];

            if (page != null)
            {
                MatchCollection matches = Regex.Matches(page, "class=\"content.*?\">(.*?)</td>", RegexOptions.Multiline);
                for (int x = 0; x < matches.Count; x++)
                {
                    StringBuilder value = new StringBuilder(matches[x].Groups[1].Value);
                    //string value = matches[x].Groups[1].Value;

                    // usuwamy śmieci z danych
                    // każde osobno tylko dla czytelności kodu
                    // normalnie można użyć:
                    // value = value.Replace("<span id=\"profile_info_name\">", "").Replace("<span></span></span>", "").Replace("<wbr></wbr>", "").Replace("&nbsp;", "").Replace("ukryty", "");
                    value = value.Replace("<span id=\"profile_info_name\">", "");
                    value = value.Replace("<span></span></span>", "");
                    value = value.Replace("<wbr></wbr>", "");
                    value = value.Replace("&nbsp;", "");
                    value = value.Replace("ukryty", "");

                    switch (x)
                    {
                        case 0: // imie
                            person[0] = value.ToString();
                            break;
                        case 1: // miejscowosc
                            person[3] = value.ToString();
                            break;
                        case 2: // nazwisko
                            person[1] = value.ToString();
                            break;
                        case 3: // wiek
                            person[4] = value.Replace(" lata", "").Replace(" lat", "").Replace(" rok", "").ToString();
                            break;
                        case 4: // nazwisko rodowe
                            person[2] = value.ToString();
                            break;
                        case 5: // plec
                            person[5] = value.ToString();
                            break;
                        case 6: // pseudo
                            person[7] = value.ToString();
                            break;
                        case 7: // numer tel.
                            person[6] = value.ToString();
                            break;
                        default:
                            break;
                    }
                }


                Match matchZnajomi = Regex.Match(page, "Znajomi <span>- (.*?)</span>", RegexOptions.Singleline);
                person[8] = matchZnajomi.Groups[1].Value;

                Match matchZdjecia = Regex.Match(page, "Galeria zdjęć <span>- (.*?)</span>", RegexOptions.Singleline);
                person[9] = matchZdjecia.Groups[1].Value;

                return person;
            }
            else
                return null;
        }

        /// <summary>
        /// Metoda zaprasza daną osobę, oczywiście na podstawie ID.
        /// </summary>
        /// <param name="ID">ID profilu na nk.pl</param>
        /// <returns>Zwraca true jeśli zaproszenie zostało !skutecznie! wysłane.
        /// W pozostałych przypadkach (błąd serwera, zablokowanie konta, dana osoba nie przyjmuje zaproszeń) zwraca false.</returns>
        public bool invitePerson(int ID)
        {
            string page = _getPage("http://nk.pl/profile/" + ID);

            if (page != null)
            {
                //Do zaproszenia potrzebujemy ticket, każda osoba ma generowany taki po wejściu na jej stronę
                //za każdym razem jest inny.
                //poniżej wybieramy ze źródła linie z ticketem
                Match match = Regex.Match(page, "<input id=\"invite_ticket\" type=\"hidden\" name=\"t\" value=\"(.*?)\">", RegexOptions.Multiline);
                string ticket = match.Groups[1].Value; // przechowuje t, np: 4d1d5006047afbdaed6dd5d6

                // wysyłamy zaproszenie
                string pageAfterPost = _sendPost("http://nk.pl/invite/" + ID, "t=" + ticket, "http://nk.pl/profile/" + ID);

                if (pageAfterPost != null)
                {
                    if (pageAfterPost.Contains("Zaproszenie zostało wysłane!"))
                        return true; // zaproszono
                    else
                    {
                        this.error[0] = "Ta osoba nie przyjmuje zaproszeń";
                        this.error[1] = "0";
                        return false;
                    }
                }
                else
                {
                    return false; // nie udane wysłaie POST'a
                }
            }
            return false; // błędny odczyt strony profilu (czyli nie można pobrać ticketa)
        }

        /// <summary>
        /// Metoda, która anuluje wszystkie (po kolei, po jednym) wysłane przez nas zaproszenia. NIE DOKOŃCZONA, aktualnie przy wywołaniu anuluje jedno zaproszenie. Aby anulować wszystkie należy użyć pętli while(nk.rejectAllSendedInvites()){}Console.WriteLine("koniec"); // lub coś w tym stylu..
        /// </summary>
        /// <returns>Zwraca true jeśli zakończona i nie ma więcej zaproszeń do anulowania. Zwraca false jeśli wystąpił jakiś błąd.</returns>
        public bool rejectAllSendedInvites()
        {
            // pobieramy stronę z zaproszeniami (wysłanymi i otrzymanymi)
            string page = _getPage("http://nk.pl/show_invites");

            if (page != null)
            {
                //linia z ticketem:
                //action="/invite/1313679/remove?t=4d260b9a8f46ea7ce4c8913d" method="post"
                //
                //zatem wyrazeniem regularnym wycinamy ticket i id
                Match ticket = Regex.Match(page, "remove\\?t=(.*?)\"");
                Match id = Regex.Match(page, "zaproszenie\" action=\"/invite/(.*?)/remove\\?t=" + ticket.Groups[1].Value, RegexOptions.Multiline);

                if (ticket.Length > 0 && id.Length > 0)
                {
                    // losujemy miejsce klikniecia buttona (x i y) (nie wiem czy nk.pl to sprawdza, ale po coś to jest...)
                    Random rnd = new Random(DateTime.Now.Millisecond);
                    int _x = rnd.Next(-10, 24);
                    int _y = rnd.Next(3, 12);

                    string pageAfterPost = _sendPost("http://nk.pl/invite/" + id.Groups[1].Value + "/remove?t=" + ticket.Groups[1].Value, "x=" + _x + "&y=" + _y, "http://nk.pl/show_invites");
                    if (pageAfterPost != null)
                    {
                        if (pageAfterPost.Contains("Zaproszenie zostało usunięte"))
                            Console.WriteLine("Usunąłem zaproszenie do " + id.Groups[1].Value);
                        return true; // wysłano zapytanie o anulowanie zaproszenia
                    }
                    return false; // błąd w wysyłaniu POST
                }
                else
                    return false; // prawdopodobnie nie ma więcej zaproszeń do anulowania
            }
            else
                return false; // błąd serwera nk.pl / blokada od nk.pl / problem z internetem
        }


    }
}
